package cn.infinite.security.interceptor;

import cn.easier.club.base.dto.ResultInfo;
import cn.infinite.security.annotation.AutoSignCheck;
import cn.infinite.security.configuration.RedisKeySet;
import cn.infinite.security.core.SafeEndPoint;
import cn.infinite.security.core.SafePointScanner;
import cn.infinite.security.core.SignatureValidator;
import cn.infinite.security.error.SignatureValidateException;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Slf4j
public class SignatureValidateInterceptor implements HandlerInterceptor {

    public static final Integer TIME_OFF_SET = 15 * 60 * 1000;

    /**
     * 安全端点扫描器
     */
    private SafePointScanner safePointScanner;

    /**
     * 签名验证器
     */
    private SignatureValidator signatureValidator;

    /**
     * 安全端点
     */
    private Map<String, AutoSignCheck> safeEndPointMap;

    private RedisTemplate<String, String> redisTemplate;

    private ValueOperations<String, String> valueOperations;


    public SignatureValidateInterceptor(SafePointScanner safePointScanner, SignatureValidator signatureValidator,
                                        RedisTemplate redisTemplate) {
        this.safePointScanner = safePointScanner;
        this.signatureValidator = signatureValidator;
        this.redisTemplate = redisTemplate;
        this.valueOperations = this.redisTemplate.opsForValue();
        this.init();
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
            IOException {
        String requestURI = request.getRequestURI();
        log.info("请求URI=>{}", requestURI);
        AutoSignCheck autoSignCheck = this.safeEndPointMap.get(requestURI);
        if (autoSignCheck == null) {
            return true;
        }
        if (autoSignCheck.enableResistReplayAttack()) {
            //如果该端点需要 进行重放攻击检测
            String uniqueId = request.getHeader(autoSignCheck.uniqueIdHeaderName());
            if (StringUtils.isBlank(uniqueId)) {
                log.error("请求头中缺少 uniqueID");
                this.responseResult(response, new ResultInfo("error", "请求头中缺少 uniqueID", null));
                return false;
            }
            String key = RedisKeySet.REPLAY_ATTACK_KEY_PREFIX.concat(uniqueId);
            Long aLong = Long.valueOf(uniqueId);
            if (Math.abs(aLong - System.currentTimeMillis()) > TIME_OFF_SET) {
                log.error("时间戳超时=>{}", aLong);
                this.responseResult(response, new ResultInfo("error", "时间戳超时", null));
                return false;
            }
            Boolean check = this.valueOperations.setIfAbsent(key, uniqueId);
            if (!check) {
                log.error("检测为重放攻击=>{}", uniqueId);
                this.responseResult(response, new ResultInfo("error", "检测为重放攻击", null));
                return false;
            }
            //设置时间戳过期
            this.redisTemplate.expire(key, TIME_OFF_SET * 2, TimeUnit.MILLISECONDS);
        }

        log.info("请求安全端点，开始自动验签=>{}", requestURI);

        boolean validateResult = false;

        String errorMessage = "签名验证失败";

        try {
            validateResult = this.signatureValidator.validate(request, autoSignCheck);
            log.info("签名验证结果=>{}", validateResult);
        } catch (SignatureValidateException e) {
            log.error("签名验证失败=>{}", e.getMessage());
            errorMessage = e.getMessage();
        }

        if (!validateResult) {
            ResultInfo<String> resultInfo = new ResultInfo<>("error", errorMessage, null);
            this.responseResult(response, resultInfo);
        }
        return validateResult;
    }

    public void init() {
        //扫描安全端点
        Collection<SafeEndPoint> safeEndPoints = this.safePointScanner.collect();
        this.safeEndPointMap = new HashMap<>(safeEndPoints.size());
        safeEndPoints.forEach(safeEndPoint -> {
            safeEndPointMap.put(safeEndPoint.getPointPath(), safeEndPoint.getAnnotation());
        });
    }

    /**
     * 响应客户端
     *
     * @param response
     * @param resultInfo
     * @throws IOException
     */
    private void responseResult(HttpServletResponse response, ResultInfo resultInfo) throws IOException {
        response.setStatus(HttpStatus.SC_UNAUTHORIZED);
        response.getWriter().write(JSON.toJSONString(resultInfo));
        response.getWriter().close();
    }
}
